﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using System.Reflection;
using JackyCode.Mock.Exceptions;

namespace JackyCode.Mock.Test
{
    [TestFixture]
    public class TestMock
    {
        private class MockDataStub : IMockData
        {
            public IList<int> MergeMethodExeTimes { get; set; }
            public IList<int> ExecuteResult { get; set; }
            public int ExcuteTimes { get; set; }

            public MockDataStub()
            {
                ExcuteTimes = 1;
                MergeMethodExeTimes = new List<int>();
                ExecuteResult = new List<int>();
            }

            
            public void Merge(MethodData methodData, object returnVal)
            {
                MergeMethodExeTimes.Add(1);
            }

            public void Add(MethodInfo methodInfo, object returnVal)
            {
                MergeMethodExeTimes.Add(1);
            }

            public object GetReturnVal(MethodData methodData)
            {
                return null;
            }

            public bool Contains(MethodData methodData)
            {
                return true;
            }

            public int Count
            {
                get { return 0; }
            }

            public int GetExecuteTimes(MethodData methodData)
            {
                ExecuteResult.Add(1);
                return ExcuteTimes;
            }

            public object GetReturnVal(MethodInfo methodInfo, IList<object> arguments)
            {
                throw new NotImplementedException();
            }

            #region IMockData Members


            public object GetParameterVal(int pIndex, MethodInfo methodInfo, IList<object> arguments)
            {
                throw new NotImplementedException();
            }

            #endregion

            #region IMockData Members


            public void SetOutParamterVal(MethodInfo methodInfo, IList<object> arguments)
            {
                throw new NotImplementedException();
            }

            #endregion
        }

        private class MockObjectFactoryStub<TEntity> : IMockObjectFactory<TEntity> 
            where TEntity:class
        {
            public IList<int> Result { get; set; }

            public MockObjectFactoryStub()
            {
                Result = new List<int>();
            }

            public IMockInterceptor MockInterceptor { get; set; }

            public TEntity CreateObject()
            {
                Result.Add(1);
                return null;
            }
        }

        private Mock<IService> iMock;
        private Mock<Service> cMock;
        private MockDataStub iMockData;
        private MockDataStub cMockData;

        [SetUp]
        public void Initialize()
        {
            iMock = new Mock<IService>(new MockDataStub(),new MockObjectFactoryStub<IService>());
            cMock = new Mock<Service>(new MockDataStub(), new MockObjectFactoryStub<Service>());
            iMockData =(MockDataStub)iMock.MockData;
            cMockData = (MockDataStub)cMock.MockData; 
        }

        [Test]
        public void NonArgsConstructor()
        {
            iMock = new Mock<IService>();
            Assert.AreEqual(typeof(MockDataImpl), iMock.MockData.GetType());
            Assert.AreEqual(typeof(MockObjectFactoryImpl<IService>), iMock.MockObjectFactory.GetType());
        }

        [Test]
        public void TestObjectProp()
        {
            object iObj = iMock.Object;
            Assert.AreEqual(1, ((MockObjectFactoryStub<IService>)iMock.MockObjectFactory).Result.Count);
            object cObj = cMock.Object;
            Assert.AreEqual(1, ((MockObjectFactoryStub<Service>)cMock.MockObjectFactory).Result.Count);
        }

        [Test]
        public void TestVirMethodSetUp()
        {
            ISetUp<string> iMethodSetUp =  iMock.SetUp(service => service.IReturnStrMethod());
            Assert.AreEqual(1, iMockData.MergeMethodExeTimes.Count);
        }

        [Test]
        [ExpectedException(ExpectedException = typeof(MockException),
            ExpectedMessage = "The method [ReturnStrNonVirMethod] should be virtual can setup!")]
        public void TestNonVirtualMethodSetUp()
        {
            ISetUp<string> iMethodSetUp = cMock.SetUp(service => service.ReturnStrNonVirMethod());
        }

        [Test]
        public void TestReturns()
        {
            iMock.SetUp(service => service.IReturnStrMethod()).Returns("Hello");
            Assert.AreEqual(2, iMockData.MergeMethodExeTimes.Count);

            cMock.SetUp(service => service.CReturnBoolMethod()).Returns(false);
            Assert.AreEqual(2, cMockData.MergeMethodExeTimes.Count);
        }

        [Test]
        [ExpectedException(ExpectedException = typeof(MockException),
            ExpectedMessage = "The method never excuted!")]
        public void TestVerifyMethodNeverCall()
        {
            iMockData.ExcuteTimes = 0;
            iMock.Verify(service => service.IReturnStrMethod());
            Assert.AreEqual(1, iMockData.ExecuteResult.Count);
        }

        [Test]
        public void TestVerifyNonAssignTimes()
        {
            iMockData.ExcuteTimes = 1;
            iMock.Verify(service => service.IReturnBoolMethod());
            Assert.AreEqual(1, iMockData.ExecuteResult.Count);

        }

        [Test]
        public void TestVerifyByTimes()
        {
            iMockData.ExcuteTimes = 2;
            iMock.Verify(service => service.IReturnBoolMethod(), 2);
            Assert.AreEqual(1, iMockData.ExecuteResult.Count);
        }

        [Test]
        [ExpectedException(ExpectedException = typeof(MockException),
            ExpectedMessage = "Except times:2,but actual times:3")]
        public void TestVerifyByTimesWithThrowException()
        {
            iMockData.ExcuteTimes = 3;
            iMock.Verify(service => service.IReturnBoolMethod(), 2);
            Assert.AreEqual(1, iMockData.ExecuteResult.Count);
        }

    }
}
